pragma Ada_2012;
with Ada.Numerics.Discrete_Random;
package body Generic_Array is

   function "+" (Left : Index_Type; Right : Count_Type) return Index_Type
   is
      pragma Inline("+");
   begin
      return Index_Type'Val(Index_Type'Pos(Left) + Right);
   end "+";

   procedure Swap (left, right : in out Element_Type) 
   is
      temp : Element_Type := left;
   begin
      left := right;
      right := temp;
   end Swap;

   procedure Generic_Swap (I, J : Index_Type) 
   is
   begin
      Swap(Source(I), Source(J));
   end Generic_Swap;

   function Generic_Find (
      Source : Array_Type;
      Value : Element_Type
      ) return Boolean
   is
   begin
      for i in Source'Range loop
         if Source(i) = Value then
            if Result /= null then
               Result.all := i;
            end if;
            return True;
         end if;
      end loop;
      return False;
   end Generic_Find;

   function Generic_Find_If (
      Source : Array_Type
      ) return Boolean
   is
   begin
      for i in Source'Range loop
         if Predicate(Source(i)) then
            if Result /= null then
               Result.all := i;
            end if;
            return True;
         end if;
      end loop;
      return False;
   end Generic_Find_If;

   function Generic_Search (
      Source : Array_Type;
      Pattern : Array_Type
      ) return Boolean
   is
   begin
      if Pattern'Length = 0 then
         return False;
      end if;
      for i in Source'Range loop
         exit when Source(i .. Source'Last)'Length < Pattern'Length;
         declare
            k : Index_Type := i;
            m : Natural := Pattern'Length;
         begin
            for Item of Pattern loop
               exit when Source(k) /= Item;
               m := m - 1;
               if m = 0 then
                  if Result /= null then
                     Result.all := i;
                  end if;
                  return True;
               end if;
               if k /= Source'Last then
                  k := Index_Type'Succ(k);
               end if;
            end loop;
         end;
      end loop;
      return False;
   end Generic_Search;

   function Generic_Find_End (
      Source : Array_Type;
      Pattern : Array_Type
      ) return Boolean
   is
   begin
      if Pattern'Length = 0 then
         return False;
      end if;
      for i in reverse Source'Range loop
         exit when Source(Source'First .. i)'Length < Pattern'Length;
         declare
            k : Index_Type := i;
            m : Natural := Pattern'Length;
         begin
            for Item of reverse Pattern loop
               exit when Source(k) /= Item;
               m := m - 1;
               if m = 0 then
                  if Result /= null then
                     Result.all := k;
                  end if;
                  return True;
               end if;
               if k /= Source'First then
                  k := Index_Type'Pred(k);
               end if;
            end loop;
         end;
      end loop;
      return False;
   end Generic_Find_End;

   function Generic_Find_First_Of (
      Source : Array_Type;
      Any : Array_Type
      ) return Boolean
   is
   begin
      for i in Source'Range loop
         if (for some Item of Any => Item = Source(i)) then
            if Result /= null then
               Result.all := i;
            end if;
            return True;
         end if;
      end loop;
      return False;
   end Generic_Find_First_Of;

   function Generic_Adjacent_Find (
      Source : Array_Type
      ) return Boolean
   is
   begin
      if Source'Length < 2 then
         return False;
      end if;
      for i in Source'First .. Index_Type'Pred(Source'Last) loop
         if Source(i) = Source(Index_Type'Succ(i)) then
            if Result /= null then
               Result.all := i;
            end if;
            return True;
         end if;
      end loop;
      return False;
   end Generic_Adjacent_Find;

   function Count (
      Source : Array_Type;
      Item : Element_Type
      ) return Count_Type
   is
      Result : Count_Type := 0;
   begin
      for who of Source loop
         if who = Item then
            Result := Result + 1;
         end if;
      end loop;
      return Result;
   end Count;

   function Generic_Count_If (
      Source : Array_Type
      ) return Count_Type
   is
      Result : Count_Type := 0;
   begin
      for who of Source loop
         if Predicate(who) then
            Result := Result + 1;
         end if;
      end loop;
      return Result;
   end Generic_Count_If;

   function Generic_Mismatch (
      Left : Array_Type;
      Right : Array_Type
      ) return Index_Pair_Type
   is
      Result : Index_Pair_Type := (Left'First, Right'First);
   begin
      while Result.First <= Left'Last and Result.Second <= Right'Last loop
         exit when Left(Result.First) /= Right(Result.Second);
         Result.First := Index_Type'Succ(Result.First);
         Result.Second := Index_Type'Succ(Result.Second);
      end loop;
      return Result;
   end Generic_Mismatch;

   function Generic_Equal (
      Left : Array_Type;
      Right : Array_Type
      ) return Boolean
   is
      c  : Count_Type := Left'Length;
      k1 : Index_Type := Left'First;
      k2 : Index_Type := Right'First;
   begin
      if c /= Right'Length then
         return False;
      end if;
      for i in 1 .. c loop
         if Left(k1) /= Right(k2) then
            return False;
         end if;
         exit when i = c;
         k1 := Index_Type'Succ(k1);
         k2 := Index_Type'Succ(k2);
      end loop;
      return True;
   end Generic_Equal;

   function Generic_Quick_Select (
      List : in out Array_Type;
      N    : Count_Type
      ) return Element_Type
   is
      pivot : Index_Type := List'First;
      procedure Swap_Index is new Generic_Swap(List);
   begin
      for I in List'First .. Index_Type'Pred(List'Last) loop
         if not (List(I) > List(List'Last)) then
            Swap_Index(I, pivot);
            pivot := Index_Type'Succ(pivot);
         end if;
      end loop;
      Swap_Index(pivot, List'Last);
      declare
         L : Array_Type renames List(List'First .. Index_Type'Pred(pivot));
         R : Array_Type renames List(pivot .. List'Last);
      begin
         if N = L'Length + 1 then
            return List(pivot);
         elsif N <= L'Length then
            return Generic_Quick_Select(L, N);
         else
            return Generic_Quick_Select(R, N - L'Length);
         end if;
      end;
   end Generic_Quick_Select;

   function Generic_Search_N (
      Source : Array_Type;
      Count : Count_Type;
      Value : Element_Type
      ) return Boolean
   is
   begin
      for i in Source'Range loop
         exit when Source(i .. Source'Last)'Length < Count;
         declare
            Last : Index_Type := Index_Type'Val(Index_Type'Pos(i) + Count - 1);
         begin
            if (for all c of Source(i .. Last) => c = Value) then
               if Result /= null then
                  Result.all := i;
               end if;
               return True;
            end if;
         end;
      end loop;
      return False;
   end Generic_Search_N;

   function To_Flags (S : String) return Replace_Flags 
   is
      pragma Inline(To_Flags);
   begin
      return Replace_Flags'(
            rf_All       => (for some c of S => c in 'g' | 'G'),
            rf_Translate => (for some c of S => c in 'i' | 'I'));
   end To_Flags;

   procedure Generic_Replace (
      Source : in out Array_Type;
      Old_Value : Element_Type;
      New_Value : Element_Type
      )
   is
   begin
      for Item of Source loop
         if Item = Old_Value then
            Item := New_Value;
         end if;
      end loop;
   end Generic_Replace;

   procedure Generic_Replace_If (
      Source : in out Array_Type;
      New_Value : Element_Type
      )
   is
   begin
      for Item of Source loop
         if Predicate(Item) then
            Item := New_Value;
         end if;
      end loop;
   end Generic_Replace_If;

   function Generic_Replace_Copy (
      Source : Array_Type;
      Old_Value : Element_Type;
      New_Value : Element_Type
      ) return Array_Type
   is
      Result : Array_Type := Source;
   begin
      for Item of Result  loop
         if Item = Old_Value then
            Item := New_Value;
         end if;
      end loop;
      return Result;
   end Generic_Replace_Copy;

   function Generic_Replace_Copy_If (
      Source : Array_Type;
      New_Value : Element_Type
      ) return Array_Type
   is
      Result : Array_Type := Source;
   begin
      for Item of Result  loop
         if Predicate(Item) then
            Item := New_Value;
         end if;
      end loop;
      return Result;
   end Generic_Replace_Copy_If;

   procedure Generic_Generate (Source : in out Array_Type)
   is
   begin
      for Item of Source loop
         Item := Generator;
      end loop;
   end Generic_Generate;

   procedure Generic_Generate_N (
      Source : in out Array_Type;
      N : Count_Type
      )
   is
      c : Count_Type := 0;
   begin
      for i in Source'Range loop
         exit when c = N;
         Source(i) := Generator;
         c := c + 1;
      end loop;
   end Generic_Generate_N;

   procedure Generic_Remove (
      Source : in out Array_Type;
      Value : Element_Type;
      Last : out Index_Type'Base
      )
   is
   begin
      Last := Index_Type'Base'Pred(Source'First);
      for Item of Source loop
         if Item /= Value then
            Last := Index_Type'Base'Succ(Last);
            Source(Last) := Item;
         end if;
      end loop;
   end Generic_Remove;

   procedure Generic_Remove_If (
      Source : in out Array_Type;
      Last : out Index_Type'Base
      )
   is
   begin
      Last := Index_Type'Base'Pred(Source'First);
      for Item of Source loop
         if not Predicate(Item) then
            Last := Index_Type'Base'Succ(Last);
            Source(Last) := Item;
         end if;
      end loop;
   end Generic_Remove_If;

   function Generic_Remove_Copy (
      Source : Array_Type;
      Value : Element_Type
      ) return Array_Type
   is
      B : Array_Type(Index_Type'Succ(Source'First) .. Index_Type'Succ(Source'Last));
      Last : Index_Type := Source'First;
   begin
      for Item of Source loop
         if Item /= Value then
            Last := Index_Type'Succ(Last);
            B(Last) := Item;
         end if;
      end loop;
      return B(B'First .. Last);
   end Generic_Remove_Copy;

   function Generic_Remove_Copy_If (
      Source : Array_Type
      ) return Array_Type
   is
      B : Array_Type(Index_Type'Succ(Source'First) .. Index_Type'Succ(Source'Last));
      Last : Index_Type := Source'First;
   begin
      for Item of Source loop
         if not Predicate(Item) then
            Last := Index_Type'Succ(Last);
            B(Last) := Item;
         end if;
      end loop;
      return B(B'First .. Last);
   end Generic_Remove_Copy_If;

   procedure Generic_Unique (
      Source : in out Array_Type;
      Last : out Index_Type
      )
   is
   begin
      Last := Source'First;
      if Source'First < Index_Type'Last then
         for i in Index_Type'Succ(Source'First) .. Source'Last loop
            if Source(i) /= Source(Last) then
               Last := Index_Type'Succ(Last);
               Source(Last) := Source(i);
            end if;
         end loop;
      end if;
   end Generic_Unique;

   function Generic_Unique_Copy (
      Source : Array_Type
      ) return Array_Type
   is
   begin
      if Source'Length = 0 then
         return Source;
      else
         declare
            B : Array_Type(Source'Range);
            Last : Index_Type := Source'First;
         begin
            B(B'First) := Source(Source'First);
            if Source'First < Index_Type'Last then
               for i in Index_Type'Succ(Source'First) .. Source'Last loop
                  if Source(i) /= B(Last) then
                     Last := Index_Type'Succ(Last);
                     B(Last) := Source(i);
                  end if;
               end loop;
            end if;
            return B(B'First .. Last);
         end;
      end if;
   end Generic_Unique_Copy;

   procedure Reverse_Array (Source : in out Array_Type)
   is
   begin
      if Source'Length > 1 then
         declare
            i : Index_Type := Source'First;
            j : Index_Type := Source'Last;
         begin
            while i /= j loop
               declare
                  T : Element_Type := Source(i);
               begin
                  Source(i) := Source(j);
                  Source(j) := T;
                  i := Index_Type'Succ(i);
                  exit when i = j;
                  j := Index_Type'Pred(j);
               end;
            end loop;
         end;
      end if;
   end Reverse_Array;

   function Reverse_Copy (Source : Array_Type) return Array_Type
   is
   begin
      if Source'Length > 1 then
         declare
            i : Index_Type := Source'First;
            B : Array_Type(Source'Range);
         begin
            for Item of reverse B loop
               Item := Source(i);
               exit when i = Source'Last;
               i := Index_Type'Succ(i);
            end loop;
            return B;
         end;
      else
         return Source;
      end if;
   end Reverse_Copy;

   procedure Rotate (
      Source : in out Array_Type;
      M : Index_Type
      )
   is
      middle : Index_Type := M;
      i : Index_Type := Source'First;
      j : Index_Type := M;
   begin
      while i /= j loop
         declare
            T : Element_Type := Source(i);
         begin
            Source(i) := Source(j);
            Source(j) := T;
         end;
         i := Index_Type'Succ(i);
         if j = Source'Last then
            j := middle;
         else
            j := Index_Type'Succ(j);
            if i = middle then
               middle := j;
            end if;
         end if;
      end loop;
   end Rotate;

   function Rotate_Copy (
      Source : Array_Type;
      M : Index_Type
      ) return Array_Type
   is
   begin
      return Source(M .. Source'Last) & Source(Source'First .. Index_Type'Pred(M));
   end Rotate_Copy;

   function Generic_Max_Index (
      Source : Array_Type
      ) return Index_Type
   is
      result : Index_Type := Source'First;
   begin
      if Source'First < Index_Type'Last then
         for i in Index_Type'Succ(Source'First) .. Source'Last loop
            if Source(result) < Source(i) then
               result := i;
            end if;
         end loop;
      end if;
      return result;
   end Generic_Max_Index;

   function Generic_Min_Index (
      Source : Array_Type
      ) return Index_Type
   is
      result : Index_Type := Source'First;
   begin
      if Source'First < Index_Type'Last then
         for i in Index_Type'Succ(Source'First) .. Source'Last loop
            if Source(i) < Source(result) then
               result := i;
            end if;
         end loop;
      end if;
      return result;
   end Generic_Min_Index;

   function Generic_Max_Element (
      Source : Array_Type
      ) return Element_Type 
   is
      result : Index_Type := Source'First;
   begin
      if Source'First < Index_Type'Last then
         for i in Index_Type'Succ(Source'First) .. Source'Last loop
            if Source(result) < Source(i) then
               result := i;
            end if;
         end loop;
      end if;
      return Source(result);
   end Generic_Max_Element;

   function Generic_Min_Element(
      Source : Array_Type
      ) return Element_Type
   is
      result : Index_Type := Source'First;
   begin
      if Source'First < Index_Type'Last then
         for i in Index_Type'Succ(Source'First) .. Source'Last loop
            if Source(i) < Source(result) then
               result := i;
            end if;
         end loop;
      end if;
      return Source(result);
   end Generic_Min_Element;

   procedure Random_Shuffle (Source : in out Array_Type)
   is
      package Discrete_Random is new Ada.Numerics.Discrete_Random(Count_Type);
      use Discrete_Random;
      g : Generator;
   begin
      Reset(g);
      for i in reverse Source'Range loop
         declare
            subtype r is Index_Type range Source'First .. i;
            k : Index_Type := Source'First + Random(g) mod r'Range_Length;
         begin
            Swap(Source(i), Source(k));
         end;
      end loop;
   end Random_Shuffle;

   procedure Generic_Partition (
      Source : in out Array_Type; 
      Right_Start : out Index_Type
      )
   is
      first : Index_Type := Source'First; 
      last  : Index_Type := Source'Last;
   begin
      loop
         declare
            h : Array_Type renames Source(first .. last);
            count : Count_Type := 0;
         begin
            for i in h'Range loop
               if not Predicate(h(i)) then
                  first := i;
                  count := count + 1;
               end if;
            end loop;
            exit when count = 0;
         end;
         declare
            t : Array_Type renames Source(Index_Type'Succ(first) .. last);
            count : Count_Type := 0;
         begin
            for i in reverse t'Range loop
               if Predicate(t(i)) then
                  last := i;
                  count := count + 1;
               end if;
            end loop;
            exit when count = 0;
            Swap(Source(first), Source(last));
            first := t'First;
         end;
      end loop;
      Right_Start := first;
   end Generic_Partition;

   function Generic_Sum (
      Source : Array_Type
      ) return Element_Type
   is
      Result : Element_Type := Source(Source'First);
   begin
      if Source'Length > 1 then
         for Item of Source(Index_Type'Succ(Source'First) .. Source'Last) loop
            Result := Result + Item;
         end loop;
      end if;
      return Result;
   end Generic_Sum;

   function Generic_Dot_Product (
      A : Array_Type;
      B : Array_Type
      ) return Array_Type
   is
      R_First : Index_Type := Index_Type'Val(0);
      subtype L is Count_Type range 0 .. A'Length - 1;
   begin
      return R : Array_Type(R_First .. R_First + L'Last) do
         for I in L loop
            R(R_First + I) := A(A'First + I) * B(B'First + I);
         end loop;
      end return; 
   end Generic_Dot_Product;

   function Generic_Inner_Product (
      A : Array_Type;
      B : Array_Type
      ) return Element_Type
   is
      A_I : Index_Type := A'First;
      B_I : Index_Type := B'First;
      Result : Element_Type := A(A_I) * B(B_I);
   begin
      while A_I < A'Last and B_I < B'Last loop
         A_I := Index_Type'Succ(A_I);
         B_I := Index_Type'Succ(B_I);
         Result := Result + A(A_I) * B(B_I);
      end loop;
      return Result;
   end Generic_Inner_Product;

   function Generic_Accumulate (
      Source : Array_Type
      ) return Element_Type
   is
      Result : Element_Type := Source(Source'First);
   begin
      if Source'Length > 1 then
         for Item of Source(Index_Type'Succ(Source'First) .. Source'Last) loop
            Result := Binary_Operation(Result, Item);
         end loop;
      end if;
      return Result;
   end Generic_Accumulate;

   function Generic_Is_Sorted (
      Source : Array_Type
      ) return Boolean
   is
      Last : Index_Type;
   begin
      if Source'Length < 2 then
         return True;
      end if;
      Last := Index_Type'Pred(Source'Last);
      return (for all i in Source'First .. Last => Source(i) < Source(Index_Type'Succ(i)));
   end Generic_Is_Sorted;

   function Starts_With (
      Source : Array_Type;
      Pattern : Array_Type
      ) return Boolean
   is
      i : Index_Type := Source'First;
   begin
      if Source'Length < Pattern'Length then
         return False;
      end if;
      for j in Pattern'Range loop
         if Source(i) /= Pattern(j) then
            return False;
         end if;
         exit when j = Pattern'Last;
         i := Index_Type'Succ(i);
      end loop;
      return True;
   end Starts_With;

   function Ends_With (
      Source : Array_Type;
      Pattern : Array_Type
      ) return Boolean
   is
      i : Index_Type := Source'Last;
   begin
      if Source'Length < Pattern'Length then
         return False;
      end if;
      for j in reverse Pattern'Range loop
         if Source(i) /= Pattern(j) then
            return False;
         end if;
         exit when j = Pattern'First;
         i := Index_Type'Pred(i);
      end loop;
      return True;
   end Ends_With;

   function Generic_Count (
      Source : Array_Type;
      Pattern : Array_Type
      ) return Count_Type
   is
      first : Index_Type := Source'First;
      last : Index_Type := Source'First;
      result : Count_Type := 0;
   begin
      while last <= Source'Last loop
         exit when Source(last .. Source'Last)'Length < Pattern'Length;
         declare
            i : Index_Type := last;
            n : Count_Type :=  0;
         begin
            for c of Pattern loop
               exit when c /= Source(i);
               n := n + 1;
               i := Index_Type'Succ(i);
            end loop;
            if n = Pattern'Length then
               result := result + 1;
               first := i;
               last := i;
            else
               last := Index_Type'Succ(last);
            end if;
         end;
      end loop;
      return result;
   end Generic_Count;

   procedure Generic_Split (
      Source    : Array_Type;
      Delimiter : Array_Type
      )
   is
      first : Index_Type := Source'First;
      last : Index_Type := Source'First;
      index : Positive := 1;
      happy : Boolean;
   begin
      while last <= Source'Last loop
         exit when Source(last .. Source'Last)'Length < Delimiter'Length;
         declare
            i : Index_Type := last;
            n : Natural :=  0;
         begin
            for c of Delimiter loop
               exit when c /= Source(i);
               n := n + 1;
               i := Index_Type'Succ(i);
            end loop;
            if n = Delimiter'Length then
               happy := Continue(
                  Piece => Source(first .. Index_Type'Pred(last)),
                  Index => index,
                  Done  => False
                  );
               if not happy then
                  return;
               end if;
               index := index + 1;
               first := i;
               last := i;
            else
               last := Index_Type'Succ(last);
            end if;
         end;
      end loop;
      happy := Continue(Source(first .. Source'Last), index, True); 
   end Generic_Split;

   package body Generic_Split_Reader is

      first : Index_Type := Source'First;
      last : Index_Type := Source'First;

      pfirst : Index_Type := Source'First;
      plast : Index_Type := Source'Last;
      done : Boolean := False;

      procedure Reset 
      is
      begin
         first := Source'First;
         last := Source'First;
         pfirst := Source'First;
         plast := Source'Last;
         done := False;
      end Reset;

      function Next return Boolean
      is
      begin
         if done then
            return False;
         end if;
         while Source(last .. Source'Last)'Length >= Delimiter'Length loop 
            declare
               i : Index_Type := last;
               n : Natural :=  0;
            begin
               for c of Delimiter loop
                  exit when c /= Source(i);
                  n := n + 1;
                  i := Index_Type'Succ(i);
               end loop;
               if n = Delimiter'Length then
                  pfirst := first;
                  plast := Index_Type'Pred(last);
                  first := i;
                  last := i;
                  return True;
               else
                  last := Index_Type'Succ(last);
               end if;
            end;
         end loop;
         pfirst := first;
         plast := Source'Last;
         done := True;
         return True;
      end Next;

      function Piece return Array_Type 
      is
      begin
         return Source(pfirst .. plast);
      end Piece;

   end Generic_Split_Reader;

   package body Generic_Split_Pair is

      head_last : Index_Type;
      tail_first : Index_Type;

      function Split return Boolean
      is
         j : Index_Type;
      begin
         head_last := Source'Last;
         tail_first := Index_Type'Succ(Source'Last);
         for i in Source'Range loop
            j := i;
            for c of Delimiter loop
               if c /= Source(j) then
                  goto continue;
               end if;
               j := Index_Type'Succ(j);
            end loop;
            head_last := Index_Type'Pred(i);
            tail_first := j;
            return True;
            <<continue>>
         end loop;
         return False;
      end Split;

      function Head return Array_Type
      is
      begin
         return Source(Source'First .. head_last);
      end Head;

      function Tail return Array_Type 
      is
      begin
         return Source(tail_first .. Source'Last);
      end Tail;

   end Generic_Split_Pair;

   function Generic_Join (
      Source1, Source2 : Array_Type
      ) return Array_Type
   is
   begin
      return Source1 & Delimiter & Source2;
   end Generic_Join;

   procedure Generic_Merge (
      Sorted_1 : Array_Type;
      Sorted_2 : Array_Type;
      Result   : in out Array_Type
      )
   is
      i : Index_Type := Sorted_1'First;
      j : Index_Type := Sorted_2'First;
   begin
      for k in Result'Range loop
         if Sorted_1(i) < Sorted_2(j) then 
            Result(k) := Sorted_1(i);
            i := Index_Type'Succ(i);
         else
            Result(k) := Sorted_2(j);
            j := Index_Type'Succ(j);
         end if;
         if i > Sorted_1'Last then
            declare
               r : Array_Type renames Sorted_2(j .. Sorted_2'Last);
            begin
               Result(k + 1 .. k + r'Length) := r;
               exit;
            end;
         end if;
         if j > Sorted_2'Last then
            declare
               r : Array_Type renames Sorted_1(i .. Sorted_1'Last);
            begin
               Result(k + 1 .. k + r'Length) := r;
               exit;
            end;
         end if;
      end loop;
   end Generic_Merge;

   function Generic_Replace_Slice (
      Source : Array_Type;
      Pattern : Array_Type;
      Value : Array_Type;
      Flags : Replace_Flags := (others => False)
      ) return Array_Type
   is
      function R (S, P : Array_Type) return Array_Type 
      is
         j : Index_Type;
      begin
         for i in S'Range loop
            exit when S(i .. S'Last)'Length < P'Length;
            j := i;
            for c of P loop
               if c /= S(j) then
                  goto continue_outer_loop;
               end if;
               j := Index_Type'Succ(j);
            end loop;
            declare
               Head : Array_Type renames Source(S'First .. Index_Type'Pred(i));
               S_Tail : Array_Type renames S(j .. S'Last);
               Source_Tail : Array_Type renames Source(j .. S'Last);
            begin
               if Flags(rf_All) then
                  return Head & Value & R(S_Tail, P);
               else
                  return Head & Value & Source_Tail;
               end if;
            end;
            <<continue_outer_loop>>  
         end loop;
         return Source(S'Range);
      end R;
   begin
      if Flags(rf_Translate) then
         declare
            S : Array_Type := Source;
            P : Array_Type := Pattern;
         begin
            Translate(S);
            Translate(P);
            return R(S, P);
         end;
      else
         return R(Source, Pattern);
      end if;
   end Generic_Replace_Slice;

   package body Generic_Append is

      Index : Index_Type := Data'First;
      Good : Boolean := Data'First = Index_Type'First;

      procedure Reset
      is
      begin
         Index := Data'First;
      end Reset;

      function Is_Empty return Boolean 
      is
      begin
         return Index = Data'First;
      end Is_Empty;

      function Length return Count_Type
      is
      begin
         if Is_Empty then
            return 0;
         end if;
         return Data(Data'First .. Index_Type'Pred(Index))'Length;
      end Length;

      procedure Append (X : Array_Type)
      is
      begin
         for Item of X loop
            Append(Item);
         end loop;
      end Append;

      procedure Append (X : Element_Type)
      is
      begin
         Data(Index) := X;
         Index := Index_Type'Succ(Index);
      end Append;

      function View return Array_Type
      is
      begin
         return Data(Data'First .. Index_Type'Pred(Index));
      end View;

   end Generic_Append;

   procedure Generic_Destroy (P : in out Pointer)
   is
   begin
      if P /= null then
         for Item of P.all loop
            Destroy(Item);
         end loop;
         Free(P);
      end if;
   end Generic_Destroy;

end Generic_Array;
